use std::{borrow::Cow, time::Duration};

use async_trait::async_trait;
use base_data::channel::{ChannelState, EndReason, OriginateWay};
use tools::{
	error::Error,
	utils::{current_data_time_with_zone, gen_id::get_now_duration, mobile_num::load_mobile_num},
	ResultNull,
};
use tracing::{debug, error, info, trace};

use crate::{
	business::{
		bus_trait::Business,
		called_rule::handle_called_rule,
		extra::{has_extra, Extra},
		inbound400::{
			bus_all_call::SaveCalleds,
			context::{BusinessState, InboundRunContext},
			logic::{
				all_busy::AllBusy, call_tips::CallTips, leave_message::LeaveMessage,
				no_set_turnnum::NotTurnNumFound,
			},
		},
		CallerSendRule, TurnnumCallType,
	},
	config::business_config,
	db::persistence::InboundDBO,
	link_to_sip::{
		answer, bridge_channel, kill_channel, originate, play_sound_file_to_caller,
		stop_play_bell,
	},
	CallingLogType,
};

use super::talk::Talk;

#[derive(Debug, Clone)]
///顺序呼叫的实现代码
pub struct Sequence;
#[async_trait]
impl Business for Sequence {
	type C = InboundRunContext;

	async fn play_back_stop(&self, context: &mut InboundRunContext, _channel_id: u32) -> ResultNull {
		//如果还未左右连接,则重复放音.
		if !context.is_bridge() {
			//再次播放彩铃的动作。走到这里一定是已经播放过的。所以前置的动作都不要
			play_sound_file_to_caller(
				context.caller_channel(),
				&context.node_info.conn_begin_bell_path,
			)?;
		}

		Ok(())
	}

	async fn change_call_state(
		&self,
		context: &mut InboundRunContext,
		_id: u32,
		state: ChannelState,
	) -> ResultNull {
		//当前只有被叫存在呼叫状态修改的情况..主叫都是主动在修改此状态,不需要再次处理.
		//有可能会收到多次此事件.
		debug!("被叫执行状态进行修改,现state:{:?}", state);

		//如果是183,最后一个直接透传动作
		if state == ChannelState::Early
				&& context.node_info.turn_nums.is_empty()
				&& context.node_info.conn_begin_bell_path == business_config().default_wait_bell_path
				//如果开通呼叫提示功能,目前不进行透传
				&& !has_extra(context.flag, Extra::CallTips)
		{
			if let Some(o_ch) = context.called_channel() {
				debug!(
					"收到183,并且有媒体,并且后续没有再要呼叫的内容,同时未设置等待音,是要放音,透传看一下"
				);

				stop_play_bell(context.caller_channel())?;
				bridge_channel(context.caller_channel(), o_ch.id())?;
				context.business_state = BusinessState::Talk; //透传
			} else {
				error!("没有拿到呼出通道的消息")
			}
		}

		Ok(())
	}

	async fn check(&self, context: &mut Self::C) -> ResultNull {
		//呼叫超时.如果检查值为0,则不进行此检查
		if !context.check_time.is_zero() && get_now_duration() >= context.check_time {
			let Some(o_ch) = context.called_channel() else {
				error!("进入呼叫超时,但未设置呼叫通道");
				return Ok(());
			};

			kill_channel(o_ch, EndReason::InitiativeHangup)?;
		}

		Ok(())
	}

	async fn kill_all(&self, context: &mut InboundRunContext) -> ResultNull {
		//收到挂断消息，同时清除全局呼叫记录
		SaveCalleds::remove_called(&context.called_num()).await;

		Ok(())
	}

	async fn hangup(
		&self,
		context: &mut InboundRunContext,
		channel_id: u32,
		reason: EndReason,
	) -> ResultNull {
		info!("收到挂断消息,id:{channel_id}");

		//收到挂断消息，同时清除全局呼叫记录
		SaveCalleds::remove_called(&context.turn_num.num).await;

		//有时候送过来的挂断是之前呼叫进行的挂断。
		if !(channel_id == context.caller_id() || channel_id == context.called_id()) {
			error!("这个挂断消息非当前呼入呼出通道的,忽略");
			return Ok(());
		}

		//有几种可能: 未接通主叫挂断.未接通被叫挂断(分同时呼叫于非同时呼叫?),已接通主被叫挂断,一方挂断后的另一方挂断
		match context.business_state {
			//未接通主叫挂断
			BusinessState::Prepare | BusinessState::StartBusiness | BusinessState::WaitDtmf => {
				info!("还未进行呼叫的主叫挂断。");
				context.business_state = BusinessState::End(EndReason::CallerPending);
			}
			//未接通挂断
			BusinessState::StartCallOutbound => {
				if channel_id == context.caller_id() {
					info!("呼叫过程当中主叫挂断");
					InboundDBO::record_calling_log(
						context,
						CallingLogType::NoAnswer,
						EndReason::CallerPending,
						Some(&context.called_num()),
						Some(context.node_info.node_id),
						&current_data_time_with_zone(),
						context.turn_num.call_type,
					)
					.await?;

					//如果已经接通,退出
					match &context.business_state {
						BusinessState::End(_) => debug!("已经完成,不再进行计费"),
						_ => context.business_state = BusinessState::End(EndReason::TalkEnd),
					}
				} else {
					info!("呼叫过程当中被叫挂断");

					//挂断的部分进行呼叫记录
					InboundDBO::record_calling_log(
						context,
						CallingLogType::NoAnswer,
						reason,
						Some(&context.turn_num.num),
						Some(context.turn_num.node_id),
						&context.now_call_time,
						context.turn_num.call_type,
					)
					.await?;

					//发起下一个呼叫
					self.call_turn_num(context).await?;
				}
			}
			BusinessState::Talk => {
				error!("这里不应该收到这些消息...{:?}", &context.business_state)
			}
			BusinessState::End(_) => {
				debug!("收到挂断信息,业务已经进入结束状态,所以不再进行处理.");
			}
		}

		Ok(())
	}

	async fn answer(&self, context: &mut InboundRunContext, channel_id: u32) -> ResultNull {
		info!(
			"收到了通道应答,id:{:?},当前状态:{:?}",
			&channel_id, &context
		);

		if context.business_state == BusinessState::Talk {
			debug!("已经开始通话,再收到应答就没有用了.");
			return Ok(());
		}

		if channel_id == context.caller_id() {
			//主叫进行应答
			if context.called_state() != ChannelState::Answered {
				trace!("主叫应答,但被叫未接通.放在这里等后续操作.");
			} else {
				//被叫已经应答,绑定.如果已经进入通话,这里状态应该改变的
				self.pre_talk_handle(context).await?;
			}
		} else {
			//如果不是主叫,判定为被叫
			//被叫进行了应答
			//主叫已经应答,绑定.未应答的,进行接通,并且
			if context.caller_state() < ChannelState::Answered {
				answer(context.caller_channel())?;
			}

			self.pre_talk_handle(context).await?;
		}

		Ok(())
	}

	async fn start_call(&self, context: &mut InboundRunContext) -> ResultNull {
		debug!("呼叫号码启动.参数:{:?}", context);
		let node_id = context.node_info.node_id;
		context.check_time = Duration::ZERO;
		//拿号码信息
		context.node_info.turn_nums = InboundDBO::get_node_turnnum_info(
			node_id,
			&context.target(),
			&context.caller_num(),
			has_extra(context.flag, Extra::ToSipTran),
		)
		.await?;

		//当节点没有任何号码被取出
		if context.node_is_empty() {
			//根据情况是跳转留言还是跳转提示
			context.next_handler = if has_extra(context.flag, Extra::VoiceMaxbox) {
				Some(&LeaveMessage)
			} else {
				Some(&NotTurnNumFound)
			};

			//借用错误进行跳转
			return Err(Error::ReplaceNode);
		}

		if !context.is_answered_caller() && context.node_info.immconnect {
			context.start_charge();
			answer(context.caller_channel())?;
		}

		play_sound_file_to_caller(
			context.caller_channel(),
			&context.node_info.conn_begin_bell_path,
		)?;

		//判断主被叫相同,并且只有一个被叫。退出
		if context.node_info.turn_nums.len() == 1
			&& context.caller_num() == context.node_info.turn_nums[0].num
		{
			info!("被叫只有一个,并且与主叫相同,退出");

			context.business_state = BusinessState::End(EndReason::CallSelf);
			return Ok(());
		}

		//进行第一次呼叫,后续的呼叫是否继续,将根据返回的数据进行判断
		self.call_turn_num(context).await?;

		Ok(())
	}
}

impl Sequence {
	///当两边都应答以后,进行的操作
	#[inline]
	async fn pre_talk_handle(&self, context: &mut InboundRunContext) -> ResultNull {
		stop_play_bell(context.caller_channel())?;

		//进行呼叫记录,接通的也要进行记录
		InboundDBO::record_calling_log(
			context,
			CallingLogType::Bridge,
			EndReason::TalkEnd,
			Some(&context.turn_num.num),
			Some(context.turn_num.node_id),
			&context.call_start_time,
			context.turn_num.call_type.clone(),
		)
		.await?;

		//准备跳转处理节
		//如果存在坐席播报功能,换节点操作
		context.start_charge();
		if has_extra(context.flag, Extra::CallTips) {
			debug!("此号码开启了坐席播报功能,换操作节点");
			//根据情况是跳转留言还是跳转提示
			context.next_handler = Some(&CallTips);
		} else {
			context.next_handler = Some(&Talk);
		}

		Err(Error::ReplaceNode)
	}

	/// 向指定的转接号码进行呼叫
	async fn call_turn_num(&self, context: &mut InboundRunContext) -> ResultNull {
		//重置呼叫超时时间.0则不会进行检查.
		context.check_time = Duration::ZERO;
		if context.node_is_empty() {
			stop_play_bell(context.caller_channel())?;

			//根据情况是跳转留言还是跳转提示
			if has_extra(context.flag, Extra::VoiceMaxbox) {
				context.next_handler = Some(&LeaveMessage);
			} else {
				context.next_handler = Some(&AllBusy);
			}

			//借用错误进行跳转
			return Err(Error::ReplaceNode);
		} else {
			context.business_state = BusinessState::StartCallOutbound;
		}

		while !context.node_is_empty() {
			context.turn_num = context.node_info.turn_nums.remove(0);
			context.set_start_called_info();
			let turn_num = &context.turn_num;
			info!("开始一个外呼动作.呼叫的信息:{:?}", turn_num);

			//将需要呼的放入全局通话记录,如果返回true代表已经存在,
			//是中继功能或sip呼叫不进行处理
			if !has_extra(context.flag, Extra::ToRelay) && SaveCalleds::add_called(&turn_num.num).await {
				info!("号码{}已经存在呼叫中,跳过。。", turn_num.num);
				//挂断的部分进行呼叫记录
				InboundDBO::record_calling_log(
					context,
					CallingLogType::NoAnswer,
					EndReason::CalledIsCalling,
					Some(&turn_num.num),
					Some(context.turn_num.node_id),
					&context.call_start_time,
					crate::business::TurnnumCallType::CallWithIsp,
				)
				.await?;

				//跳出,进行下一次呼叫
				continue;
			}

			//是否需要发送来电名片判断
			if let Err(e) = self.send_calling_card(context).await {
				error!("发送来电名片失败,错误信息:{}", e);
			}
			let called_num = match handle_called_rule(&context.turn_num.num).await {
				Ok(num) => num,
				Err(e) => {
					error!("处理呼叫号码失败,错误信息:{}", e);
					Cow::Borrowed(context.turn_num.num.as_str())
				}
			};

			//发送呼叫信息给连接.发起呼叫
			originate(
				context.caller_channel(),
				&self.convert_caller_number(context),
				&self.caller_name(context),
				&called_num,
				match turn_num.call_type {
					TurnnumCallType::CallWithIsp | TurnnumCallType::Unknow => OriginateWay::IspGateway,
					TurnnumCallType::CallExGW => {
						let Some(addr) = &context.sip_tran_info else {
							error!("呼叫{:?}时,sip中继信息为空", turn_num);
							continue;
						};
						OriginateWay::with_external(addr.clone())
					}
					TurnnumCallType::CallTerminal => OriginateWay::RegisterUser,
				},
			)?;

			context.check_time = get_now_duration() + Duration::from_secs(turn_num.wait_seconds as u64);
		}

		Ok(())
	}

	#[inline]
	async fn send_calling_card(&self, context: &InboundRunContext) -> ResultNull {
		if context.turn_num.call_type != TurnnumCallType::CallWithIsp {
			debug!("除向运营商网关发送闪信,其余不发送");
			return Ok(());
		}
		let Some(called) = load_mobile_num(&context.called_num()) else {
			debug!("只向手机号发送闪信...号码:{}", context.called_num());

			return Ok(());
		};

		if has_extra(context.flag, Extra::CallingCard) {
			if let Some(ccc) = InboundDBO::get_calling_card_config(context, &called).await? {
				let (billing_id, rcode) = ccc.send_request_shtel(context, &called).await?;
				InboundDBO::calling_card_start(context, &called, billing_id, rcode, &ccc.mp_info_name)
					.await?;
			} else {
				error!("没有对应的来电名片配置项.跳过.");
			}
		}

		Ok(())
	}

	///根据规则,转换主叫号码
	#[inline]
	fn convert_caller_number<'a>(&'a self, context: &'a InboundRunContext) -> Cow<str> {
		match (
			context.turn_num.call_type,
			business_config().caller_rule.into(),
		) {
			//如果是sip用户，发送正常主叫
			(TurnnumCallType::CallTerminal, _) | (_, CallerSendRule::SourceCaller) => {
				Cow::Borrowed(&context.caller_num())
			}
			(_, CallerSendRule::Target) => Cow::Borrowed(&context.target()),
			(_, CallerSendRule::Specified(s)) => Cow::Borrowed(s),
		}
	}

	///主叫号码的显示名称
	#[inline]
	fn caller_name<'a>(&'a self, context: &'a InboundRunContext) -> Cow<str> {
		if context.turn_num.call_type == TurnnumCallType::CallTerminal {
			Cow::Owned(format!("{}{}", context.caller_num(), context.caller_area.1))
		} else {
			Cow::Borrowed(&context.caller_num())
		}
	}
}
